#lang racket
(require "note.rkt")
(require "image.rkt")
(require "animation.rkt")
(require "partition.rkt")
(require rsound)
(provide monde%)

;monde.rkt
;version 1.0.0
(define monde%
  (class object%
    ;----------------------------------- Attributs ----------------------------;
    (define TICKS-PAR-SECONDE 48)
    (init-field (partitions (list))) ;liste de partitions <=> liste de liste de notes
    (init-field (temps-attente TICKS-PAR-SECONDE))
    (init-field (images (list)))
    (init-field (animations (list)))
    (init-field (ps (make-pstream)))
    (init-field (fini? #f))
    
    ;----------------------------------- Getters ------------------------------;
    (define/public (get-ticks-par-seconde) TICKS-PAR-SECONDE)
    (define/public (get-images) images)
    (define/public (get-animations) animations)
    (define/public (get-partitions) partitions)
    (define/public (get-ps) ps)
    (define/public (is-over?) fini?)
    
    ;----------------------------------- Methodes -----------------------------;
    ;partition-vide?
    ;retourne #t si la partition est vide
    (define/public (partition-vide? partition)
      (null? (send partition get-notes)))
    
    ;afficher
    ;affiche une description du monde dans la console
    (define/public (afficher)
      (printf "monde : \n Partition -> ~a \n Images -> ~a \n Animations -> ~a
               \n Temps-attente -> ~a \n Fini? -> ~a \n ps -> ~a"
              partition 
              (for/list ([i (in-list images)]) (send i getImage))
              (for/list ([a (in-list animations)]) (send a toString)) 
              temps-attente fini? ps))
    
    ;lire-partitions
    ;lit toutes les partitions à un moment m
    (define/public (lire-partitions)
      ;On teste si les partitions sont toutes finies
      (if (toutes-partitions-finies?) 
          (fermer-animation)
          (begin
            (for [(p (in-list partitions))]
              (lire-partition p))
            (jouer-animations))))
    
    
    (define/private (toutes-partitions-finies?)
      (andmap (λ (p) (and (null? (send p get-notes)) (equal? (send p get-temps-attente) 0)))
              partitions))
    
    
    
    
    
    
    ;lire-partition
    ;lit le contenu de la partition au temps courant, et modifie le monde
    ;en conséquence.
    (define/public (lire-partition partition)
      (define temps-attente (send partition get-temps-attente))
      (cond ((and (= temps-attente 0) (partition-vide? partition)) void)
            ((= temps-attente 0) (lire-note partition))
            (else (send partition set-temps-attente (- temps-attente 1)))
            ))
    
    ;lire-note
    ;lit la première note de la partition p
    (define/public (lire-note partition)
      (define note-courante (car (send partition get-notes)))
      (define images-eligibles   ;On définit la liste des images qui attendent d'être animées et qui sont du bon type
        (if (equal? 'null (send note-courante get-image-class));Si la note n'est pas liée à une image, aucune ne sera elligible
            '()
            (filter (λ (img) (and
                              (not (send img isAnimated?))
                              (equal? (send img getClass) (send note-courante get-image-class))))
                    images)))
      (if (not (null? images-eligibles))         ;SI la liste d'images disponibles n'est pas vide, on lie l'animation désirée
          (begin                                 ;à une image tirée au hasard dans cette liste.
            (bind-animation-to-image
             (list-ref images-eligibles (random (length images-eligibles)))
             (send note-courante get-animation-class)
             (deduire-temps-attente (send note-courante get-type)))
            (pstream-play ps (send note-courante get-son)) ;puis on joue le son
            (send partition set-notes (cdr (send partition get-notes))) ;on enlève la note jouée de la partition
            (send partition set-temps-attente (deduire-temps-attente (send note-courante get-type)))) ;on remet le temps d'attente à la bonne valeur
          
          (begin                                  ;SINON on ne fait que jouer le son
            (pstream-play ps (send note-courante get-son))
            (send partition set-notes (cdr (send partition get-notes)))
            (send partition set-temps-attente (deduire-temps-attente (send note-courante get-type))))))
    
    ;jouer-animations
    ;joue la liste des animations
    (define/public (jouer-animations)
      ;ETAPE 1 - Epuration de la liste des animation (suppression des animations finies)
      (set! animations (filter (λ (anim) (not(send anim isFinished?))) animations))
      ;ETAPE 2 - Lancement des animations vivantes
      (for ([animation-courante (in-list animations)])
        (send animation-courante jouer)))
    
    ;bind-animation-to-image
    ;Lie une nouvelle animation à une image existante en fonction d'une animation-type puis ajoute l'animation
    ;à la liste des animations présentes dans le monde
    (define/public (bind-animation-to-image mon-image anim-class ma-duree)
      (cond
        [(equal? anim-class 'Animation-changer-couleur%) (begin
                                                           (define nouvelle-animation (new Animation-changer-couleur% 
                                                                                           (duree ma-duree) 
                                                                                           (rest ma-duree)
                                                                                           (img mon-image)))
                                                           (set! animations (cons nouvelle-animation animations)))]
        [else (printf "DONG\n")]))
    
    ;deduire-temps-attente
    ;Déduis le temps d'attente depuis un type de note
    (define/public (deduire-temps-attente type)
      (case type
        [("noire") TICKS-PAR-SECONDE]
        [("blanche") (* TICKS-PAR-SECONDE 2)]
        [("noire-pointee") (* TICKS-PAR-SECONDE 1.5)]
        [("croche") (/ TICKS-PAR-SECONDE 2)]
        [("croche3") (/ TICKS-PAR-SECONDE 3)]
        [("croche4") (/ TICKS-PAR-SECONDE 4)]
        [else TICKS-PAR-SECONDE]))
    
    ;Déduit le temps d'execution total d'une partition en nombre de cycle
    (define/public (deduire-temps-total partition)
      (define temps 0)
      (for [(note (in-list (send partition get-notes)))]
        (set! temps (+ temps (deduire-temps-attente (send note get-type)))))
      temps)
    
    (define/public (deduire-temps-x-premieres-notes x partition)
      (define temps (send partition get-temps-attente))
      (define notes (send partition get-notes))
      (for [(i (in-range x))]
        (set! temps (+ temps
                       (deduire-temps-attente (send (list-ref notes i) get-type)))))
      temps)
    
    ;fermer-animation
    ;méthode privée chargée de déclencher le processus de fin du monde
    (define/private (fermer-animation)
      (set! fini? #t))
    
    (super-new)))